import math as Math
import Core.Random as Random

# S:Underly
# X:Strike
# t:Time
# v:Volatility
# p:option price


def ImpliedVolatility(s, x, t, r, p, optionType, putCall, optionPrice, n = 0, eps = 0.001):

    if(optionPrice == "Binomial"):
        n = 100;
    elif(optionPrice == "MonteCarlo"):
        n = 100000;

    num = 0.0;
    num2 = 10.0;
    optionValue = 0.0;
    v = 0.0;
    while (Math.fabs((num2 - num)) > eps):

        v = num + ((num2 - num) / 2.0);

        if optionPrice == "BlackScholes":
            optionValue = BS(s, x, t, v, r, putCall);
        elif optionPrice == "Binomial":
            optionValue = BM(s, x, t, v, r, putCall, n);
        elif optionPrice == "MonteCarlo":
            optionValue = MC(s, x, t, v, r, putCall, n);

        if (optionValue > p):
            num2 = v;
        else:
            num = v;
    #While loop

    return v;


#Black Scholes
def BS(s, x, t, v, r, putCall):
    #print("Call BS")
    if v <= 0.01:
        v = 0.01
    if putCall == "Call":
        if t <= 0:
            return Call(s,x)
        return ((s * N(d1(s, x, t, v, r))) - ((x * Math.exp(-r * t)) * N(d2(s, x, t, v, r))));
    elif putCall == "Put":
        if t <= 0:
            return Put(s,x)
        return ((-s * N(-d1(s, x, t, v, r))) + ((x * Math.exp(-r * t)) * N(-d2(s, x, t, v, r))));
    return 0.0;


#Binominal Tree
def BM(s, x, t, v, r, putCall, n):

    f = 0.0;
    x = u(t, s, n);
    num3 = d(t, s, n);
    p1 = p(t, s, n, r);
    for i in range(0,n):
        f += Binom(i, n, p1) * Payoff((s * Math.pow(x, i)) * Math.pow(num3, (n - i)), x, putCall);

    return PV4(f, r, t);


#Monte Carlo
def MC(s, x, t, v, r, putCall, n):
    num = (r - ((0.5 * s) * s)) * t;
    num2 = s * Math.sqrt(t);
    f = 0.0;
    for i in range(0,n):
        f += Payoff(s * Math.exp(num + (num2 * Random.Gaus())), x, putCall);
    f /= n;
    return PV4(f, r, t);


def Binom(m, n, p):
    return ((C(m, n) * Math.pow(p, m)) * Math.pow(1.0 - p, (n - m)));

#Combination
#排列组合的组合
def C( m,  n):
    return (Fact(n) / (Fact(m) * Fact(n - m)));

#Call Intrisic value
def Call(s,  x):
    #return Math.Max(0.0, (S - X));
    if s - x > 0:
        return s- x
    return 0

def Delta( s,  x,  t,  v,  r, putCall):

    if putCall == "Call":
        return N(d1(s, x, t, v, r));

    if putCall == "Put":
        return (N(d1(s, x, t, v, r)) - 1.0);

    return 0.0;


def d1(s,  x,  t,  v,  r):
    #a =Math.log(Math.e,s / x)
    value = ((Math.log(s / x, Math.e) + ((r + ((v * v) / 2.0)) * t)) / (v * Math.sqrt(t)))
    return value


def d2( s,  x,  t,  v,  r):
    return (d1(s, x, t, v, r) - (v * Math.sqrt(t)));


#阶乘
def Fact(n):
    num = 1.0;
    for i in range(0,n):
        num *= i;
    return num;


#Future Value
#P:Sopt Price
#r: rate
#n: period
def FV1(p,  r,  n):
    return (p * (1.0 + (r * n)));


#Future Value Continous Interest rate
def FV2(p,  r,  n):
    return (p * Math.pow(1.0 + r, n))


def Gamma( s,  x,  t,  v,  r):
    return (n(d1(s, x, t, v, r)) / ((s * v) * Math.sqrt(t)));


#概率密度的积分
def N(z):
    num7 = 0;
    num = 2.506628;
    num2 = 0.3193815;
    num3 = -0.3565638;
    num4 = 1.7814779;
    num5 = -1.821256;
    num6 = 1.3302744;
    if ((z <= 0.0) and (z != 0.0)):
        num7 = -1.0;
    else:
        num7 = 1.0;
    num8 = 1.0 / (1.0 + ((0.2316419 * num7) * z));
    return (0.5 + (num7 * (0.5 - ((Math.exp((-z * z) / 2.0) / num) * (num8 * (num2 + (num8 * (num3 + (num8 * (num4 + (num8 * (num5 + (num8 * num6)))))))))))));


#概率密度
def n(z):
    return ((1.0 / Math.sqrt(6.2831853071795862)) * Math.exp((-0.5 * z) * z));


#Present Value
def PV1( F,  r,  n):
    return (F / (1.0 + (r * n)));

#Present Value
def PV2( F,  r,  n):
    return (F / Math.pow(1.0 + r, n));

def PV3( F,  r,  n,  m):
    return (F / Math.pow(1.0 + (r / m), n * m));

def PV4( F,  r,  n):
    return (F / Math.exp(r * n));

#期末支付
def Payoff( s,  x,  putCall):
    if putCall == "Call":
        return Call(s, x);
    if putCall == "Put":
        return Put(s, x);
    return 0.0;


#Put Intrisic Value
def Put( s,  x):
    #return Math.Max(0.0, (X - S));
    if x - s > 0:
        return x-s
    return 0


#二叉树中的U
def u(t, s, n):
    return Math.exp(s * Math.sqrt(t / n));

#二叉树中的D
def d(t, s, n):
    return Math.exp(-s * Math.sqrt(t / n));


def p(t, s, n, r):
    num = FV2(1.0, r, t / n);
    num2 = u(t, s, n);
    num3 = d(t, s, n);
    return ((num - num3) / (num2 - num3));


def Theta( s,  x,  t,  v,  r,  putCall) :
    if putCall == "Call":
        a = ((-s * n(d1(s, x, t, v, r))) * v)
        b = (2.0 * Math.sqrt(t))
        c = ((r * x) * Math.exp(-r * t))
        d = d2(s, x, t, v, r)
        return ((((-s * n(d1(s, x, t, v, r))) * v) / (2.0 * Math.sqrt(t))) - (((r * x) * Math.exp(-r * t)) * N(d2(s, x, t, v, r))));
    elif putCall == "Put":
        return ((((-s * n(d1(s, x, t, v, r))) * v) / (2.0 * Math.sqrt(t))) + (((r * x) * Math.exp(-r * t)) * N(-d2(s, x, t, v, r))));
    return 0.0;


def Vega( s,  x,  t,  v,  r):
    return ((s * Math.sqrt(t)) * n(d1(s, x, t, v, r)));



